﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Data;
using Microsoft.WindowsAzure.StorageClient;
using crmbl.Azure.Globals;
using crmbl.Azure.Tables.Globals;
using crmbl.Azure.Metadata.Globals;

namespace crmbl.Azure.Diagnostics.Logs.Processor
{
    public class LogsProcessor : IDisposable
    {
        private const String LOG_TABLENAME = "WADLogsTable";

        String _storageAccountName;
        String _storageAccountKey;
        Guid _storageAccountId;

        int _flushToDatabaseCount;

        TableServiceContext _tableContext;

        ApplicationHelper _app;

        DataTable _toProcessData;

        #region Constructors

        public LogsProcessor(Guid storageAccountId, String storageAccountName, String StorageAccountPrimaryKey, String targetDataSourceConnectionString, int flushToDatabaseCount = 1000)
        {
            _storageAccountName = storageAccountName;
            _storageAccountKey = StorageAccountPrimaryKey;

            _storageAccountId = storageAccountId;

            _flushToDatabaseCount = flushToDatabaseCount;

            // Constuct a new application helper
            _app = new ApplicationHelper(targetDataSourceConnectionString);

            // Initiate a new Table Context
            _tableContext = AzureTableHelper.GetTableContext(_storageAccountName, _storageAccountKey);
        }
        #endregion

        #region Public Methods

        public void Process()
        {
            // Connect to configuration database, get the latest id
            GenericTableEntity lastUpdatedConfig = _app.GetTableObjectMetadata(_storageAccountId, LOG_TABLENAME);

            long maxPartitionKey = long.Parse(lastUpdatedConfig.PartitionKey);
            String maxRowKey = lastUpdatedConfig.RowKey;
            DateTime maxTimestamp = lastUpdatedConfig.Timestamp;

            foreach (LogEntity log in GetLogsToProcess(lastUpdatedConfig.PartitionKey, lastUpdatedConfig.RowKey, lastUpdatedConfig.Timestamp))
            {
                try
                {
                    // Write the Windows Event
                    //_app.WriteLogEntry(log);
                    AddDataToToProcess(log);

                    // Update if newer
                    if (log.Timestamp > maxTimestamp)
                    {
                        maxTimestamp = log.Timestamp;
                        maxPartitionKey = long.Parse(log.PartitionKey);
                        maxRowKey = log.RowKey;
                    }
                }
                catch (Exception ex)
                {
                    //TODO: Log an error
                    Console.WriteLine(ex.ToString());
                }
            }

            try
            {
                WriteToProcessDataToDatabase();
            }
            catch (Exception ex)
            {
                Console.WriteLine(ex.ToString());
            }

            // Update the TableDownload
            _app.UpdateTableObjectUpdate(_storageAccountId, LOG_TABLENAME, "0" + maxPartitionKey.ToString(), maxRowKey,maxTimestamp);
        }

        #endregion

        #region Bulk Data Methods

        private void AddDataToToProcess(LogEntity eventEntry)
        {
            if (_toProcessData == null)
                BuildToProcessDataTable();


            object[] o = {
                            _app.GetRoleRowId(Guid.Parse(eventEntry.DeploymentId), eventEntry.Role),
                            new DateTime(eventEntry.EventTickCount),
                            eventEntry.EventId,
                            eventEntry.Level,
                            _app.GetInstanceNumberFromFullName(eventEntry.RoleInstance),
                            eventEntry.Message
                         };

            _toProcessData.Rows.Add(o);

            if (_toProcessData.Rows.Count == _flushToDatabaseCount)
                WriteToProcessDataToDatabase();
        }

        private void BuildToProcessDataTable()
        {
            _toProcessData = new DataTable();

            _toProcessData.Columns.Add("RoleRowId", typeof(int));
            _toProcessData.Columns.Add("DateTimeUTC", typeof(DateTime));
            _toProcessData.Columns.Add("EventNumber", typeof(int));
            _toProcessData.Columns.Add("EventLevel", typeof(int));
            _toProcessData.Columns.Add("InstanceNumber", typeof(int));
            _toProcessData.Columns.Add("Message", typeof(String));

        }

        private void WriteToProcessDataToDatabase()
        {
            if (_toProcessData != null && _toProcessData.Rows.Count > 0)
                _app.WriteLogEntryBulk(_toProcessData);
        }

        #endregion

        #region Private Methods

        private List<LogEntity> GetLogsToProcess(String startingParitionKey, String startingRowKey, DateTime lastProcessedTimestampUTC, int top = 0)
        {
            IQueryable<LogEntity> eventLogsTable = _tableContext.CreateQuery<LogEntity>(LOG_TABLENAME);

            var newCounters = (from r in eventLogsTable
                               where r.PartitionKey.CompareTo(startingParitionKey) >= 0
                                    & r.Timestamp > lastProcessedTimestampUTC
                               select r);
            if (top > 0)
            {
                return newCounters.Take(top).ToList();
            }

            return newCounters.ToList();
        }

        #endregion

        #region Deconstuctors

        void IDisposable.Dispose()
        {
            try
            {
                _app.Dispose();
            }
            catch (Exception) { } //ignore exceptions

        }

        #endregion
    }
}
